package com.hy.${packageCode}.shared.autoconfigure.common;

import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.hy.${packageCode}.shared.executor.CommonTaskExecutor;
import com.hy.${packageCode}.shared.utils.SpringUtil;
import com.hy.${packageCode}.shared.executor.CommonTaskExecutor;
import com.hy.${packageCode}.shared.utils.UserAuditorHelper;
import com.hy.${packageCode}.shared.executor.CommonTaskExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;

@EnableAsync
@Import(WebMVCConfiguration.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({SharedProperties.class})
@AutoConfigureAfter(TransactionAutoConfiguration.class)
@RemoteApplicationEventScan({"com.hy.${packageCode}.shared.events"})
@EnableCaching
public class SharedAutoConfiguration {

    @Autowired
    private  SharedProperties sharedProperties;

    @Bean
    @ConditionalOnMissingBean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @EventListener(ApplicationReadyEvent.class)
    public void onReady(ApplicationReadyEvent readyEvent) {
        SpringUtil.setApplicationContext(readyEvent.getApplicationContext());
    }

    @Bean("transactionWithNew")
    @ConditionalOnBean(PlatformTransactionManager.class)
    @ConditionalOnMissingBean(name = "transactionWithNew")
    public TransactionTemplate transactionWithNew(PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager, new DefaultTransactionDefinition(TransactionDefinition
                .PROPAGATION_REQUIRES_NEW));
    }


    @Bean
    @ConditionalOnBean(ThreadPoolTaskExecutor.class)
    @Primary
    public CommonTaskExecutor commonPlatExecutor(ThreadPoolTaskExecutor taskExecutor) {
        return new CommonTaskExecutor(taskExecutor);
    }

    @Bean
    @ConditionalOnMissingBean(ThreadPoolTaskExecutor.class)
    public CommonTaskExecutor commonVirtualExecutor() {
        return new CommonTaskExecutor(Executors.newVirtualThreadPerTaskExecutor());
    }

    @Bean
    @ConditionalOnMissingBean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        restTemplateBuilder.additionalMessageConverters(messageConverter);
        return  restTemplateBuilder.build();
    }

    @Bean
    @Primary
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(
            @Value("${r"${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}"}") String pattern) {
        return jacksonObjectMapperBuilder -> {
            jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern)));
            jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(pattern)));
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public UserAuditorHelper userAuditorHelper() {
        return new UserAuditorHelper();
    }

}
